package com.wei.czz.framework.common.manager;

import com.wei.czz.common.constant.EntityConstant;
import com.wei.czz.common.dto.common.NameValue;
import com.wei.czz.common.dto.common.log.LogDto;
import com.wei.czz.common.dto.common.log.LogPageEnumDto;
import com.wei.czz.common.dto.common.log.LogStatsDto;
import com.wei.czz.common.dto.common.log.OneInterfaceStatsDto;
import com.wei.czz.common.enums.CommonEnum;
import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.tool.PageDto;
import com.wei.czz.common.utils.CacheKeyUtils;
import com.wei.czz.common.utils.CopyUtils;
import com.wei.czz.common.vo.common.log.LogFormVo;
import com.wei.czz.common.vo.common.log.LogStatsVo;
import com.wei.czz.framework.admin.service.impl.UserServiceImpl;
import com.wei.czz.framework.common.entity.LogEntity;
import com.wei.czz.framework.common.helper.CacheHelper;
import com.wei.czz.framework.common.service.LogService;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2024-07-03 10:29:45
 * className: LogManager
 * version: 1.0
 * description:
 */
@Component
@AllArgsConstructor
public class LogManager {

    private static final Logger log = LoggerFactory.getLogger(LogManager.class);

    private final UserServiceImpl userService;

    private final LogService logService;

    private final CacheHelper cacheHelper;

    public LogStatsDto getLogStats(LogStatsVo logStatsVo) {
        LogStatsDto logStatsDto = new LogStatsDto();

        /*
            获取请求日志
         */
        List<LogEntity> logList = logService.findList(logStatsVo);
        if (logList.isEmpty()) {
            logStatsDto.setDataList(Collections.emptyList());
            logStatsDto.setUrlList(Collections.emptyList());
            return logStatsDto;
        }
        if (StringUtils.isNotBlank(logStatsVo.getUrl())) {
            // 单个接口统计
            OneInterfaceStatsDto oneInterfaceStatsDto = this.getOneInterfaceStatsDto(logList);
            logStatsDto.setDataList(Collections.singletonList(oneInterfaceStatsDto));
            return logStatsDto;
        }

        Map<String, OneInterfaceStatsDto> interfaceStatsDtoMap = logList.stream().collect(Collectors.groupingBy(
                LogEntity::getUri,
                Collectors.collectingAndThen(Collectors.toList(), this::getOneInterfaceStatsDto)
        ));
        // 结果排序
        List<OneInterfaceStatsDto> dataList = interfaceStatsDtoMap.values().stream()
                .sorted(Comparator.comparing(OneInterfaceStatsDto::getRequestNum).reversed())
                .collect(Collectors.toList());
        // 获取所有请求地址
        List<String> uelList = dataList.stream().map(OneInterfaceStatsDto::getUrl).collect(Collectors.toList());
        if (dataList.size() > logStatsVo.getShowNum()) {
            dataList = dataList.subList(0, logStatsVo.getShowNum());
        }
        logStatsDto.setDataList(dataList);
        logStatsDto.setUrlList(uelList);

        return logStatsDto;
    }

    public PageDto<LogDto> getLogPageList(LogFormVo logFormVo) {

        /*
            缓存请求参数
         */
        String cacheKey = CacheKeyUtils.getParamCacheKey(EntityConstant.LOG);
        cacheHelper.cache(cacheKey, logFormVo);

        /*
            分页获取请求日志
         */
        PageDto<LogEntity> pageDto = logService.getPageList(logFormVo);
        if (pageDto.isEmpty()) {
            return pageDto.rebuild();
        }

        List<LogEntity> logList = pageDto.getList();

        // 收集用户主键
        Set<Long> userIdSet = logList.stream().map(LogEntity::getUserId)
                .filter(userId -> !CommonEnum.ZERO.getLongValue().equals(userId))
                .collect(Collectors.toSet());
        /*
            获取用户名称映射
         */
        Map<Long, String> userNameMap = userService.getNameMap(userIdSet, Function.identity());

        // 列表映射
        List<LogDto> logDtoList = CopyUtils.mapList(logList, _log -> {
            LogDto logDto = CopyUtils.map(_log, LogDto.class);
            logDto.setUserName(userNameMap.get(_log.getUserId()))
                    .setStartTime(new Date(_log.getStartTime()))
                    .setEndTime(new Date(_log.getEndTime()));
            return logDto;
        });
        // 封装返回
        return pageDto.rebuild(logDtoList);
    }

    public LogPageEnumDto getPageEnum() {

        /*
            获取请求参数
         */
        String cacheKey = CacheKeyUtils.getParamCacheKey(EntityConstant.LOG);
        LogFormVo logFormVo = cacheHelper.get(cacheKey, LogFormVo.class);

        List<NameValue<String>> typeList = new ArrayList<>();
        typeList.add(new NameValue<>(RequestMethod.GET.name(), RequestMethod.GET.name()));
        typeList.add(new NameValue<>(RequestMethod.POST.name(), RequestMethod.POST.name()));
        typeList.add(new NameValue<>(RequestMethod.PUT.name(), RequestMethod.PUT.name()));
        typeList.add(new NameValue<>(RequestMethod.DELETE.name(), RequestMethod.DELETE.name()));

        List<NameValue<Integer>> codeList = Arrays.stream(ResultEnum.values())
                .map(ResultEnum::getCode)
                .distinct()
                .map(code -> new NameValue<>(String.valueOf(code), code))
                .collect(Collectors.toList());

        LogPageEnumDto logPageEnumDto = new LogPageEnumDto();
        logPageEnumDto.setTypeList(typeList)
                .setCodeList(codeList)
                .setLogFormVo(logFormVo);
        return logPageEnumDto;
    }

    public Date getUriLastSuccessRequestTime(String uri) {
        /*
            获取接口最后一次请求成功日志
         */
        LogEntity log = logService.findUriLastSuccessRequest(uri);
        if (Objects.isNull(log)) {
            return null;
        }
        return new Date(log.getStartTime());
    }

    /**
     * 统计单个接口请求日志
     * @param logList 单个接口请求日志列表
     * @return 统计数据
     */
    private OneInterfaceStatsDto getOneInterfaceStatsDto(List<LogEntity> logList) {
        OneInterfaceStatsDto oneInterfaceStatsDto = new OneInterfaceStatsDto();

        int successNum = 0, failNum = 0, maxSuccessTime = 0, minSuccessTime = Integer.MAX_VALUE,
                totalSuccessNum = 0, maxFailTime = 0, minFailTime = Integer.MAX_VALUE, totalFailNum = 0;
        for (LogEntity _log : logList) {
            // 得到处理时长
            int time = (int) (_log.getEndTime() - _log.getStartTime());
            if (ResultEnum.SUCCESS.getCode().equals(_log.getCode())) {
                // 成功
                successNum += 1;
                maxSuccessTime = Math.max(maxSuccessTime, time);
                minSuccessTime = Math.min(minSuccessTime, time);
                totalSuccessNum += time;
            } else {
                // 失败
                failNum += 1;
                maxFailTime = Math.max(maxFailTime, time);
                minFailTime = Math.min(minFailTime, time);
                totalFailNum += time;
            }
        }
        oneInterfaceStatsDto.setUrl(logList.get(0).getUri())
                .setRequestNum(logList.size())
                .setSuccessNum(successNum)
                .setFailNum(failNum)
                .setSuccessPercent(BigDecimal.ZERO)
                .setFailPercent(BigDecimal.ZERO)
                .setMaxSuccessTime(maxSuccessTime)
                .setMinSuccessTime(minSuccessTime == Integer.MAX_VALUE ? 0 : minSuccessTime)
                .setAvgSuccessTime(0)
                .setMaxFailTime(maxFailTime)
                .setMinFailTime(minFailTime == Integer.MAX_VALUE ? 0 : minFailTime)
                .setAvgFailTime(0);
        if (successNum != 0) {
            oneInterfaceStatsDto.setSuccessPercent(
                    BigDecimal.valueOf(successNum)
                            .divide(BigDecimal.valueOf(logList.size()), 2, RoundingMode.HALF_UP)
                            .multiply(BigDecimal.valueOf(100))
                            .setScale(2, RoundingMode.UP)
                    ).setAvgSuccessTime(totalSuccessNum / successNum);
        }
        if (failNum != 0) {
            oneInterfaceStatsDto.setFailPercent(
                    BigDecimal.valueOf(failNum)
                            .divide(BigDecimal.valueOf(logList.size()), 2, RoundingMode.HALF_UP)
                            .multiply(BigDecimal.valueOf(100))
                            .setScale(2, RoundingMode.UP)
                    ).setAvgFailTime(totalFailNum / failNum);
        }
        return oneInterfaceStatsDto;
    }
}
